﻿using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace MiniFox.Data.EntityFramework
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class EntityDataQuery<TEntity> : EntityDataCommand<TEntity>, IDataQuery<TEntity> where TEntity : class
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public EntityDataQuery(DbContext context) : base(context)
        {

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        protected virtual IQueryable<TEntity> DataSorting(IQueryable<TEntity> source, IDictionary<string, bool> sortFields)
        {
            var i = 0;
            foreach (KeyValuePair<string, bool> pair in sortFields)
            {
                string sortField = pair.Key;
                bool sortDesc = pair.Value;
                string sortingDir;

                if (i <= 0)
                {
                    sortingDir = "OrderBy";
                    if (sortDesc)
                        sortingDir = "OrderByDescending";
                }
                else
                {
                    sortingDir = "ThenBy";
                    if (sortDesc)
                        sortingDir = "ThenByDescending";

                }

                PropertyInfo pi = typeof(TEntity).GetProperty(sortField, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);

                ParameterExpression param = Expression.Parameter(typeof(TEntity), sortField);
                Type[] types = new Type[2];
                types[0] = typeof(TEntity);
                types[1] = pi.PropertyType;
                Expression expr = Expression.Call(typeof(Queryable), sortingDir, types, source.Expression, Expression.Lambda(Expression.Property(param, pi.Name), param));
                var query = source.Provider.CreateQuery<TEntity>(expr);
                //Enumerable<T> ienum = query;
                source = query;
                i++;
            }
            return source;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public virtual ResultSet<TEntity> Execute(Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields)
        {
            IQueryable<TEntity> queryable = CurrentDbContext.Set<TEntity>();
            return Execute(queryable, lambda, sortFields);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <returns></returns>
        public virtual ResultSet<TEntity> Execute(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields)
        {
            IQueryable<TEntity> dataQuery;
            if (lambda == null)
            {
                dataQuery = queryable;
            }
            else
            {
                dataQuery = queryable.Where(lambda);
            }

            int rowCount = dataQuery.Count();

            if (sortFields != null)
            {
                dataQuery = this.DataSorting(dataQuery, sortFields);
            }

            ResultSet<TEntity> result = new ResultSet<TEntity>();
            result.Data = dataQuery;
            result.RowCount = rowCount;
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxRowCount"></param>
        /// <returns></returns>
        public ResultSet<TEntity> Execute(Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields, int? maxRowCount)
        {
            IQueryable<TEntity> queryable = CurrentDbContext.Set<TEntity>();
            return Execute(queryable, lambda, sortFields, maxRowCount);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <param name="maxRowCount"></param>
        /// <returns></returns>
        public virtual ResultSet<TEntity> Execute(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields, int? maxRowCount)
        {
            if (maxRowCount == null)
                return this.Execute(queryable, lambda, sortFields);
            IQueryable<TEntity> dataQuery;
            if (lambda == null)
            {
                dataQuery = queryable;
            }
            else
            {
                dataQuery = queryable.Where(lambda);
            }
            var rowCount = dataQuery.Count();
            if (sortFields != null)
            {
                dataQuery = this.DataSorting(dataQuery, sortFields);
            }
            dataQuery = dataQuery.Take(maxRowCount <= 0 ? 10 : maxRowCount.GetValueOrDefault());

            ResultSet<TEntity> result = new ResultSet<TEntity>();
            result.Data = dataQuery;
            result.RowCount = rowCount;
            return result;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public PagedResultSet<TEntity> Execute(Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields, int pageIndex, int pageSize)
        {
            IQueryable<TEntity> queryable = CurrentDbContext.Set<TEntity>();
            return Execute(queryable, lambda, sortFields, pageIndex, pageSize);

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="queryable"></param>
        /// <param name="lambda"></param>
        /// <param name="sortFields"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public PagedResultSet<TEntity> Execute(IQueryable<TEntity> queryable, Expression<Func<TEntity, bool>> lambda, IDictionary<string, bool> sortFields, int pageIndex, int pageSize)
        {
            IQueryable<TEntity> dataQuery;
            if (lambda == null)
            {
                dataQuery = queryable;
            }
            else
            {
                dataQuery = queryable.Where(lambda);
            }
            var rowCount = dataQuery.Count();
            if (sortFields != null)
            {
                dataQuery = this.DataSorting(dataQuery, sortFields);
            }
            dataQuery = dataQuery.Skip((pageIndex - 1) * pageSize).Take(pageSize);

            PagedResultSet<TEntity> result = new PagedResultSet<TEntity>(pageIndex, pageSize);
            result.RowCount = rowCount;
            result.Data = dataQuery;
            return result;
        }
    }
}
